import { Vue } from "./install.js";
import { forEach } from "./utils.js";
import ModuleCollection from "./module-collection.js";

function getNewState(store, path) {
  return path.reduce((memo, current) => {
    return memo[current];
  }, store.state);
}

function installModule(store, rootState, path, module) {
  // a/b/c/d
  // 获取moduleCollection类的实例
  let ns = store._modules.getNamespaced(path);
  console.log(rootState);
  // module.state => 放到rootState对应的儿子里
  if (path.length > 0) {
    // 这是一个儿子模块
    // 需要找到对应的父模块， 将状态声明上去
    // {name: 'zf', age: '12', a: aState}
    let parent = path.slice(0, -1).reduce((memo, current) => {
      return memo[current];
    }, rootState);
    // 对象新增属性不能导致重新更新视图
    store._withCommitting(() => {
      Vue.set(parent, path[path.length - 1], module.state);
    });
  }
  // 需要循环当前模块的
  // module._raw.getters 不符合规范
  module.forEachGetter((fn, key) => {
    // console.log(fn, key);
    store.wrapperGetters[ns + key] = function () {
      return fn.call(store, getNewState(store, path));
    };
  });
  module.forEachMutation((fn, key) => {
    // console.log(fn, key);
    store.mutations[ns + key] = store.mutations[ns + key] || [];
    store.mutations[ns + key].push((payload) => {
      let result = undefined;
      store._withCommitting(() => {
        fn.call(store, getNewState(store, path), payload);
      });
      store._subscribes.forEach((fn) =>
        fn({ type: ns + key, payload }, store.state)
      );
      return result;
    });
  });
  module.forEachAction((fn, key) => {
    // console.log(fn, key);
    store.actions[ns + key] = store.actions[ns + key] || [];
    store.actions[ns + key].push((payload) => {
      return fn.call(store, store, payload);
    });
  });
  module.forEachChildren((child, key) => {
    installModule(store, rootState, path.concat(key), child);
  });
}

function resetVM(store, state) {
  let oldVM = store._vm;
  const computed = {};
  store.getters = {};
  forEach(store.wrapperGetters, (getter, key) => {
    computed[key] = getter;
    Object.defineProperty(store.getters, key, {
      get: () => {
        return store._vm[key];
      },
    });
  });
  store._vm = new Vue({
    data: {
      $$state: state,
    },
    computed: computed,
  });
  if (store.strict) {
    // 说明是严格模式， 我要监控状态
    // 如果状态更新， 原则上，是异步更新
    store._vm.$watch(
      () => store._vm._data.$$state,
      () => {
        // console.log("ok");
        // 我希望状态变化后，直接就能监控到， watcher都是异步的？
        // sync 状态变化会立即执行，不是异步
        console.assert(
          store._committing,
          "no mutate in mutation in handler outside"
        );
      },
      { deep: true, sync: true }
    );
  }
  if (oldVM) {
    // 重新创建实例后，需要将老的实例卸载掉
    Vue.nextTick(() => {
      oldVM.$destory();
    });
  }
}

class Store {
  constructor(options) {
    // 对用户的模块进行整合
    this._modules = new ModuleCollection(options);
    // 对用户的参数进行树状格式化操作
    // console.log(this.modules, "****");
    this.wrapperGetters = {};
    // this.getters = {};
    this.mutations = {};
    this.actions = {};
    this._subscribes = [];
    this._committing = false; // 默认不是在mutation中更改的
    this.strict = options.strict;
    // 我需要将模块中的所有的getters、mutations、actions进行封装
    // 没有namespace的时候， getters都放在根上，actions、mutations会被合并数组

    // Object.defineProperty()

    // 需要知道父子关系
    let state = options.state;
    installModule(this, state, [], this._modules.root);
    // console.log(this.getters, this.mutations, this.actions);

    resetVM(this, state);
    if (options.plugins) {
      // 说明用户使用了插件
      options.plugins.forEach((plugin) => plugin(this));
    }
  }
  get state() {
    return this._vm._data.$$state;
  }

  _withCommitting(fn) {
    this._committing = true; // 如果true
    fn(); // 函数是同步的， 获取committing 就是true，如果是异步的，那么就会变成false，就会打印日志
    this._committing = false;
  }
  commit = (mutationName, payload) => {
    // 发布
    this.mutations[mutationName] &&
      this.mutations[mutationName].forEach((fn) => fn(payload));
  };

  dispatch = (actionName, payload) => {
    this.actions[actionName] &&
      this.actions[actionName].forEach((fn) => fn(payload));
  };

  subscribe(fn) {
    this._subscribes.push(fn);
  }

  replaceState(newState) {
    // 需要替换的状态
    this._withCommitting(() => {
      this._vm._data.$$state = newState; // 替换最新的状态， 赋予对象类型会被重新劫持
    });
    // 虽然替换了状态，但是mutations getter中的state在初始化的时候，已经被绑定老的状态
  }

  registerModule(path, module) {
    // module这个模块是用户自己写的，需要将用户的module转换一下
    // 最终都转换为数组
    if (typeof path == "string") path = [path];

    // [] ,  module
    this._modules.register(path, module); // 模块的注册，将用户的数据放到树中
    // 注册完毕后，再进行安装， 将用户的module重新安装
    installModule(this, this.state, path, module.newModule);

    // vuex内部重新注册的话，会重新生成实例，虽然重新安装了，只解决了状态的问题， 但是computed就丢失了
    resetVM(this, this.state);
  }
}

// state, getters, mutations, actions(modules分层)
export default Store;
